//
//  HttpRequest.m
//  AFNetworking封装
//
//  Created by 侯宝伟 on 2019/3/30.
//  Copyright © 2019 zhunjiee. All rights reserved.
//

#import "HttpRequest.h"
#import "BaseNavigationController.h"
#import "LoginViewController.h"

#define DownloadPath @"DownloadPath"

static NSString * const RequestSuccessCode = @"1";              // 请求成功
static NSString * const RequestTokenInvalidCode = @"4001";      // token失效
static NSString * const RequestFailureText = @"网络连接错误!";    // 请求失败错误信息
static NSString * const ResponseMessageKey = @"msg";            // 返回结果提示信息

@implementation HttpRequest

static id _instance = nil;
+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone {
    return _instance;
}
- (instancetype)init {
    if (self = [super init]) {
        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        manager.operationQueue.maxConcurrentOperationCount = 5;       // 最大并发数
        manager.requestSerializer.timeoutInterval = 60;     // 请求超时时间
        // json 方式请求
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        // form data 方式请求
//        [manager.requestSerializer setValue:@"application/x-www-form-urlencoded;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/plain", @"text/javascript", @"text/xml", @"image/*", nil];
        self.manager = manager;
        
        // 是否启用加解密
        self.securityRequest = NO;
        // 是否打印结果
        self.enableLog = YES;
    }
    return self;
}


#pragma mark - GET请求
- (void)getWithUrlPath:(NSString *)urlPath parameters:(id)parameters success:(SuccessBlock)success failure:(FailureBlock)failure {
    NSString *url = kStringIsEmpty(urlPath) ? API_Base_Url : [NSString stringWithFormat:@"%@%@", API_Base_Url, urlPath];
    NSDictionary *headerParams = @{
        @"Authorization" : [NSString stringWithFormat:@"Bearer %@ ",kCheckString([BWUserDefaults userToken])],
    };
    
    if (self.enableLog) {
        NSLog(@"token ====== %@", kCheckString([BWUserDefaults userToken]));
        NSLog(@"url ======= %@", url);
        NSLog(@"parameters ======= %@", parameters);
    }
    
    // 是否加解密
    NSDictionary *params = [NSDictionary dictionary];
    if (self.securityRequest) {
        // 参数AES加密
        NSString *paramStr = [BWMethod objectToJsonString:parameters];
        NSString *encodeStr = [BWSecurities aesEncrypt:paramStr];
        params = @{@"encryptedData" : encodeStr};
        if (self.enableLog) {
            NSLog(@"GET 加密后参数: %@", params);
        }
    } else {
        params = parameters;
    }
    
    kWeakSelf(self);
    [self.manager GET:url parameters:params headers:headerParams progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        kStrongSelf(self);
        if (self.enableLog) {
            NSLog(@"responseObject ======= %@", responseObject);
        }
        [self dealSuccessResultWithResponseObject:responseObject success:success failure:failure];
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        kStrongSelf(self);
        [self dealErrorResultWithError:error failure:failure];
    }];
}


#pragma mark - POST请求
- (void)postWithUrlPath:(NSString *)urlPath parameters:(id)parameters success:(SuccessBlock)success failure:(FailureBlock)failure {
    NSString *url = kStringIsEmpty(urlPath) ? API_Base_Url : [NSString stringWithFormat:@"%@%@", API_Base_Url, urlPath];
    NSDictionary *headerParams = @{
        @"Authorization" : [NSString stringWithFormat:@"Bearer %@ ",kCheckString([BWUserDefaults userToken])],
    };

    if (self.enableLog) {
        NSLog(@"header ====== h%@", self.manager.requestSerializer.HTTPRequestHeaders);
        NSLog(@"token ====== %@", kCheckString([BWUserDefaults userToken]));
        NSLog(@"url ======= %@", url);
        NSLog(@"parameters ======= %@", parameters);
    }
    
    // 是否加解密
    NSDictionary *params = [NSDictionary dictionary];
    if (self.securityRequest) {
        // 参数AES加密
        NSString *paramStr = [BWMethod objectToJsonString:parameters];
        NSString *encodeStr = [BWSecurities aesEncrypt:paramStr];
        params = @{@"encryptedData" : encodeStr};
        if (self.enableLog) {
            NSLog(@"POST 加密后参数: %@", params);
        }
    } else {
        params = parameters;
    }
    
    kWeakSelf(self);
    [self.manager POST:url parameters:params headers:headerParams progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        kStrongSelf(self);
        if (self.enableLog) {
            NSLog(@"responseObject ======= %@", responseObject);
        }
        [self dealSuccessResultWithResponseObject:responseObject success:success failure:failure];
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        kStrongSelf(self);
        [self dealErrorResultWithError:error failure:failure];
    }];
}


#pragma mark - 上传文件
- (void)uploadWithUrlPath:(NSString *)urlPath parameters:(id)parameters uploadParam:(NSArray<UploadParam *> *)uploadParams progress:(ProgressBlock)progress success:(SuccessBlock)success failure:(FailureBlock)failure {
    NSString *url = kStringIsEmpty(urlPath) ? API_Base_Url : [NSString stringWithFormat:@"%@%@", API_Base_Url, urlPath];
    NSDictionary *headerParams = @{
        @"Authorization" : [NSString stringWithFormat:@"Bearer %@ ",kCheckString([BWUserDefaults userToken])],
    };
    
    if (self.enableLog) {
        NSLog(@"token ====== %@", kCheckString([BWUserDefaults userToken]));
        NSLog(@"url ======= %@", url);
        NSLog(@"parameters ======= %@", parameters);
    }
    
    // 是否加解密
    //    NSDictionary *params = [NSDictionary dictionary];
    //    if (self.securityRequest) {
    //        // 参数AES加密
    //        NSString *paramStr = [BWMethod objectToJsonString:parameters];
    //        NSString *encodeStr = [BWSecurities aesEncrypt:paramStr];
    //        params = @{@"encryptedData" : encodeStr};
    //    } else {
    //        params = parameters;
    //    }
    //    NSLog(@"UPLOAD 加密后参数: %@", params);
    
    [self.manager POST:url parameters:parameters headers:headerParams constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        for (UploadParam *uploadParam in uploadParams) {
            [formData appendPartWithFileData:uploadParam.data name:uploadParam.name fileName:uploadParam.filename mimeType:uploadParam.mimeType];
        }
    } progress:^(NSProgress * _Nonnull uploadProgress) {
        if (progress) {
            progress(uploadProgress);
        }
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [self dealSuccessResultWithResponseObject:responseObject success:success failure:failure];
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [self dealErrorResultWithError:error failure:failure];
    }];
}


#pragma mark - 下载数据
- (void)downLoadWithUrlString:(NSString *)urlString saveFilePath:(NSString *)saveFilePath progress:(ProgressBlock)progress success:(SuccessBlock)success failure:(FailureBlock)failure {
    // 拼接缓存目录
    NSString *tempSavePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:DownloadPath];
    NSString *downloadPath = !kStringIsEmpty(saveFilePath) ? saveFilePath : tempSavePath;
    // 打开文件管理器
    NSFileManager *fileManager = [NSFileManager defaultManager];
    // 截取文件名称
    NSString *fileName = [urlString lastPathComponent];
    // 拼接文件路径
    NSString *filePath = [downloadPath stringByAppendingPathComponent:fileName];
    NSLog(@"下载文件路径: filePath === %@", filePath);
    
    // 判断文件是否存在
    BOOL isDirectory = NO;
    BOOL isExist = [fileManager fileExistsAtPath:filePath isDirectory:&isDirectory];
    if (isExist) {
        NSLog(@"文件已经存在");
        // 文件已经存在,直接返回
        if (success) {
            success(filePath);
        }
        return;
    } else {
        // 创建Download目录
        [fileManager createDirectoryAtPath:downloadPath withIntermediateDirectories:YES attributes:nil error:nil];
    }

    // 开始下载文件
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
    NSURLSessionDownloadTask *downLoadTask = [self.manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
        if (progress) {
            progress(downloadProgress);
        }
    } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
        
        // 返回文件位置的URL路径
        return [NSURL fileURLWithPath:filePath];
    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        if (error) {
            NSLog(@"下载文件出错: %@", error);
            [self dealErrorResultWithError:error failure:failure];
        } else {
            if (success) {
                NSLog(@"文件下载完成: %@", filePath.path);
                success(filePath.path);
            }
        }
    }];
    [downLoadTask resume];
}


#pragma mark - 网络状态监测
- (void)setReachabilityStatusChangeBlock:(void(^)(AFNetworkReachabilityStatus status))block {
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    [manager startMonitoring];
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
                // 未知网络
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知网络");
                break;
                // 没有网络
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"没有网络");
                break;
                // 手机自带网络
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"当前使用的是2G/3G/4G网络");
                break;
                // WIFI
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"当前在WIFI网络下");
                break;
        }
        if (block) {
            block(status);
        }
    }];
}

- (void)stopMonitoring {
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    [manager stopMonitoring];
}


#pragma mark - 返回结果处理

/// 正确结果处理方法
- (void)dealSuccessResultWithResponseObject:(id)responseObject success:(SuccessBlock)success failure:(FailureBlock)failure {
    NSDictionary *responseDict = [NSDictionary dictionary];
        if (self.securityRequest) {
            // 解密
            NSString *encryptedData = [responseObject[@"encryptedData"] description];
            NSString *decodeStr = [BWSecurities aesDecrypt:encryptedData];
            NSDictionary *responseDict = [BWMethod dictionaryWithJsonString:decodeStr];
            NSLog(@"解码后的返回结果: %@", responseDict);
        } else {
            responseDict = responseObject;
        }
        
        // 数据处理
        NSString *responseCode = [responseDict[@"code"] description];
        if ([responseCode isEqualToString:@"0"]) {
            // 结果正确
            if ([[responseDict allKeys] containsObject:@"data"]) {
                if ([responseDict[@"data"] isKindOfClass:[NSDictionary class]]) {
                    NSDictionary *dataDict = responseDict[@"data"];
                    success(dataDict);
                } else if ([responseDict[@"data"] isKindOfClass:[NSArray class]]) {
                    NSArray *dataArray = responseDict[@"data"];
                    success(dataArray);
                } else {
                    success(responseDict);
                }
            } else {
                success(responseDict);
            }
        } else if ([responseCode isEqualToString:@"4500"]) {
            // 前端开发者（我）错误
            NSString *errorText = [NSString stringWithFormat:@"%@", responseDict[@"message"]];
            failure(responseCode, errorText);
        } else if ([responseCode isEqualToString:@"4700"]) {
            //token失效 退出登录
            failure(responseCode, @"登录过期, 请重新登录");
            // 跳转到登录页面
            [self logOutAndClearUserInfo];
        } else if ([responseCode isEqualToString:@"4800"] || [responseCode isEqualToString:@"4810"]) {
            // 用户可见错误
            NSString *errorText = [NSString stringWithFormat:@"%@", responseDict[@"message"]];
            failure(responseCode, errorText);
        } else {
            // 致命错误
            NSString *errorText = @"出现不可预期的错误,请联系开发人员";
            NSLog(@"!!!!!! 出现致命错误 !!!!!!  %@ --------- %@", responseDict[@"code"], responseDict[@"message"]);
            failure(responseCode, errorText);
        }
}

/// 错误结果处理
- (void)dealErrorResultWithError:(NSError *)error failure:(FailureBlock)failure {
    failure(CustomErrorCode, RequestFailureText);
    NSLog(@"error: %@", error);
}

#pragma mark 退出登录
/// 退出登录
- (void)logOutAndClearUserInfo {
    [SVProgressHUD showInfoWithStatus:@"登录过期, 请重新登录!"];
    // 清除用户数据
    [BWUserDefaults saveUserInfo:nil];
    // 设置根控制器
    LoginViewController *vc = [[LoginViewController alloc] init];
    UIViewController *rootVC = [BWMethod getRootViewController];
    NSLog(@"rootVC ------ ====== %@", rootVC);
    if ([rootVC isKindOfClass:[UITabBarController class]]) {
        UITabBarController *tabBarVC = (UITabBarController *)rootVC;
        UINavigationController *nav = tabBarVC.selectedViewController;
        [nav pushViewController:vc animated:YES];
    } else {
        BaseNavigationController *nav = [[BaseNavigationController alloc] initWithRootViewController:vc];
        if (@available(iOS 13.0, *)) {
            UIWindow *window = [UIApplication sharedApplication].windows[0];
            [window setRootViewController:nav];
        } else {
            UIWindow *window = [UIApplication sharedApplication].delegate.window;
            [window setRootViewController:nav];
        }
    }
}
    

@end
